#ifndef GAME_ANIMATION_MAKE_TEST_H
#define GAME_ANIMATION_MAKE_TEST_H
//
// Game Animation.
// Copyright (C) 2007 Department of Computer Science, University of Copenhagen
//
#include <animation/configuration.h>

#include<OpenTissue/math/noise/improved_noise.h>
#include<OpenTissue/math/random.h>

#include <queue>
#include <vector>
#include <cassert>

namespace animation
{

  template<typename vector3_type, typename vector3_container>
  inline void create_wall( 
    vector3_type const & b0
    , vector3_type const & b1
    , vector3_type const & n0
    , vector3_type const & n1
    , vector3_container & positions
    , vector3_container & normals
    )
  {
    vector3_type normal0 = n0;
    vector3_type normal1 = n1;
    vector3_type normal2 = n1;
    vector3_type normal3 = n0;


    vector3_type dir = unit(b1-b0);


    OpenTissue::Random<typename vector3_type::value_type> random(-0.2f,0.2f);

    vector3_type middle = (b1+b0)*0.5;


    if(middle(2)>-0.5 || middle(2)<-3.5)
      return;

    vector3_type p0 = middle - dir*0.75;
    vector3_type p1 = middle + dir*0.75;

    p0(0) += random();
    p0(1) += random();
    p0(2) += random();
    p1(0) += random();
    p1(1) += random();
    p1(2) += random();


    vector3_type coord0 = p0;
    vector3_type coord1 = p1;
    vector3_type coord2 = p1+normal2*1.0;
    vector3_type coord3 = p0+normal3*1.0;
    normals.push_back( normal0  );
    normals.push_back( normal1  );
    normals.push_back( normal2  );
    normals.push_back( normal3  );
    positions.push_back( coord0  );
    positions.push_back( coord1  );
    positions.push_back( coord2  );
    positions.push_back( coord3  );
  }



  template<typename vector3_type, typename vector3_container>
  inline void create_tower( 
    vector3_type const & p0
    , vector3_type const & p1
    , vector3_type const & p2
    , vector3_type const & n0
    , vector3_type const & n1
    , vector3_type const & n2
    , vector3_container & positions
    , vector3_container & normals
    )
  {
    vector3_type e10 = p1 - p0;
    vector3_type e21 = p2 - p1;
    vector3_type e02 = p0 - p2;

    vector3_type b0 = p0 + 0.25*e10;
    vector3_type b1 = p0 + 0.75*e10;

    vector3_type b2 = p1 + 0.25*e21;
    vector3_type b3 = p1 + 0.75*e21;

    vector3_type b4 = p2 + 0.25*e02;
    vector3_type b5 = p2 + 0.75*e02;

    vector3_type w0 = unit(0.75*n0 + 0.25*n1);
    vector3_type w1 = unit(0.25*n0 + 0.75*n1);

    vector3_type w2 = unit(0.75*n1 + 0.25*n2);
    vector3_type w3 = unit(0.25*n1 + 0.75*n2);

    vector3_type w4 = unit(0.75*n2 + 0.25*n0);
    vector3_type w5 = unit(0.25*n2 + 0.75*n0);

    create_wall(b0,b3, w0,w3, positions,normals);
    create_wall(b1,b4, w1,w4, positions,normals);
    create_wall(b2,b5, w2,w5, positions,normals);
  }



  template<typename vector3_type, typename vector3_container>
  inline void subdivide_surface( 
    vector3_type const & p0
    , vector3_type const & p1
    , vector3_type const & p2
    , vector3_type const & n0
    , vector3_type const & n1
    , vector3_type const & n2
    , int level
    , int max_level
    , vector3_container & positions
    , vector3_container & normals
    )
  {
    if(level==max_level)
    {
      create_tower( p0, p1, p2, n0, n1, n2, positions, normals);
    }
    else
    {
      vector3_type e10 = p1 + p0;
      vector3_type e21 = p2 + p1;
      vector3_type e02 = p0 + p2;

      vector3_type p10 = e10*0.5;
      vector3_type p21 = e21*0.5;
      vector3_type p02 = e02*0.5;

      vector3_type n10 = unit(n1 + n0);
      vector3_type n21 = unit(n1 + n2);
      vector3_type n02 = unit(n2 + n0);
      // p2---- p21---- p1
      // |         / 
      // p02    p10
      //  |  /
      // p0
      subdivide_surface( p0, p10, p02,  n0, n10, n02,level+1, max_level, positions, normals);
      subdivide_surface(p10,  p1, p21, n10,  n1, n21,level+1, max_level, positions, normals);
      subdivide_surface(p02, p10, p21, n02, n10, n21,level+1, max_level, positions, normals);
      subdivide_surface(p02, p21,  p2, n02, n21,  n2,level+1, max_level, positions, normals);
    }
  }




  template<typename vector3_type,typename real_type,typename geometry_type>
  void create_imposter(
    unsigned int const & idx
    , vector3_type const & coord0
    , vector3_type const & coord1
    , vector3_type const & coord2
    , vector3_type const & coord3
    , vector3_type const &  normal0
    , vector3_type const &  normal1
    , vector3_type const &  normal2
    , vector3_type const &  normal3
    , real_type const &u0
    , real_type const &v0
    , real_type const &u1
    , real_type const &v1
    , geometry_type & field
    )
  {
    typedef typename geometry_type::vertex_type  vertex_type;

    unsigned int first_vertex_idx = idx*4;
    vertex_type & p0 = field.get_vertex(first_vertex_idx + 0);
    vertex_type & p1 = field.get_vertex(first_vertex_idx + 1);
    vertex_type & p2 = field.get_vertex(first_vertex_idx + 2);
    vertex_type & p3 = field.get_vertex(first_vertex_idx + 3);

    p0.m_coord = coord0;
    p1.m_coord = coord1;
    p2.m_coord = coord2;
    p3.m_coord = coord3;

    p0.m_normal = normal0;
    p1.m_normal = normal1;
    p2.m_normal = normal2;
    p3.m_normal = normal3;

    //--- For color tinting or shifting
    p0.m_color[0] = 0.1; p0.m_color[1] = 1.0; p0.m_color[2] = 0.1; p0.m_color[3] = 0.25;
    p1.m_color[0] = 0.1; p1.m_color[1] = 1.0; p1.m_color[2] = 0.1; p1.m_color[3] = 0.25;
    p2.m_color[0] = 0.1; p2.m_color[1] = 1.0; p2.m_color[2] = 0.1; p2.m_color[3] = 0.25;
    p3.m_color[0] = 0.1; p3.m_color[1] = 1.0; p3.m_color[2] = 0.1; p3.m_color[3] = 0.25;
    //--- Use  texcoords to select the wanted subregion of the grass
    //--- texture atlas
    p0.m_tex0[0] = u0;  p0.m_tex0[1] = v0;
    p1.m_tex0[0] = u1;  p1.m_tex0[1] = v0;
    p2.m_tex0[0] = u1;  p2.m_tex0[1] = v1;
    p3.m_tex0[0] = u0;  p3.m_tex0[1] = v1;

    //---- setup special effects data, winddirection and grass stiffness!!!
    p0.m_tex2[0] = 1.0; p0.m_tex2[1] = 0.0; p0.m_tex2[2] = 0.0; p0.m_tex2[3] = 0.5;
    p1.m_tex2[0] = 1.0; p1.m_tex2[1] = 0.0; p1.m_tex2[2] = 0.0; p1.m_tex2[3] = 0.5;
    p2.m_tex2[0] = 1.0; p2.m_tex2[1] = 0.0; p2.m_tex2[2] = 0.0; p2.m_tex2[3] = 0.5;
    p3.m_tex2[0] = 1.0; p3.m_tex2[1] = 0.0; p3.m_tex2[2] = 0.0; p3.m_tex2[3] = 0.5;

    unsigned int j = idx*6;
    field.get_index(j+0) = first_vertex_idx+0;
    field.get_index(j+1) = first_vertex_idx+1;
    field.get_index(j+2) = first_vertex_idx+2;

    field.get_index(j+3) = first_vertex_idx+0;
    field.get_index(j+4) = first_vertex_idx+2;
    field.get_index(j+5) = first_vertex_idx+3;
  }

  template<typename geometry_type,typename real_type>
  void make_test(
    geometry_type const & surface
    , geometry_type & field
    , real_type halfwidth
    , real_type height
    )
  {
    typedef typename geometry_type::vertex_type          vertex_type;
    typedef typename geometry_type::vector3_type         vector3_type;

    //--- First determine planting positions of all grass clumps.
    std::vector<vector3_type> positions;
    std::vector<vector3_type> normals;

    for(unsigned int i=0;i<surface.indices();i=i+3)
    {
      vertex_type const & g0 = surface.get_vertex(surface.get_index(i));
      vertex_type const & g1 = surface.get_vertex(surface.get_index(i+1));
      vertex_type const & g2 = surface.get_vertex(surface.get_index(i+2));
      subdivide_surface( g0.m_coord, g1.m_coord, g2.m_coord, g0.m_normal, g1.m_normal, g2.m_normal, 0, 2, positions, normals);
    }

    //--- Make sure that we have allocated space for all the imposter geometries.
    unsigned int cnt_vertices = positions.size();
    unsigned int cnt_imposters = cnt_vertices/4;
    unsigned int cnt_triangles = cnt_imposters*2;
    unsigned int cnt_indices = cnt_triangles*3;


    if(field.vertices() != cnt_vertices)
      field = geometry_type(cnt_vertices, cnt_indices);

    //--- Finally we create the imposters.
    for(unsigned int i=0;i<cnt_imposters;++i)
    {
      //--- Pick a set of texture coords in the texture atlas (assumed to be 1-by-4)
      //real_type u0 = (i%4)*0.25;
      //real_type v0 = 0.08;
      //real_type u1 = 0.25 + (i%4)*0.25;
      //real_type v1 = 1.0;


      real_type u0 = 1*0.25;
      real_type u1 = 0.25 + u0;
      real_type v0 = 0.08;
      real_type v1 = 1.0;

      vector3_type normal0 = normals[i*4];
      vector3_type normal1 = normals[i*4+1];
      vector3_type normal2 = normals[i*4+2];
      vector3_type normal3 = normals[i*4+3];

      vector3_type coord0 = positions[i*4];
      vector3_type coord1 = positions[i*4+1];
      vector3_type coord2 = positions[i*4+2];
      vector3_type coord3 = positions[i*4+3];

      create_imposter(
        i
        , coord0
        , coord1
        , coord2
        , coord3
        , normal0
        , normal1
        , normal2
        , normal3
        , u0
        , v0
        , u1
        , v1
        , field
        );
    }
  }

} // namespace animation

// GAME_ANIMATION_MAKE_TEST_H
#endif
